#!/bin/bash
set -f -o pipefail
# disabled for now, script is not compatible with this option
# set -e 

source "/usr/local/etc/babun.instance"
# source "$babun_tools/script.sh"
source "$babun_tools/check.sh"
source "$babun_tools/procps.sh"
source ~/.pact/pact.repo

# this script requires some packages
WGET=`which wget 2> /dev/null`
BZIP2=`which bzip2 2> /dev/null`
TAR=`which tar 2> /dev/null`
GAWK=`which awk 2> /dev/null`
XZ=`which xz 2> /dev/null`
if test "-$WGET-" = "--" || test "-$BZIP2-" = "--" || test "-$TAR-" = "--" \
  || test "-$GAWK-" = "--" || test "-$XZ-" = "--"
then
  echo You must install wget, tar, gawk, bzip2 and xz to use pact.
  exit 1
fi

if [ -z "$USER_AGENT" ]; then
  USER_AGENT="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
fi
  

function usage()
{
  echo pact: Installs and removes Cygwin packages.
  echo ""
  echo "Usage:"
  echo "  \"pact install <package names>\" to install given packages"
  echo "  \"pact remove <package names>\" to remove given packages"
  echo "  \"pact update <package names>\" to update given packages"
  echo "  \"pact show\" to show installed packages"
  echo "  \"pact find <patterns>\" to find packages matching patterns"
  echo "  \"pact describe <patterns>\" to describe packages matching patterns"
  echo "  \"pact packageof <commands or files>\" to locate parent packages"
  echo "  \"pact invalidate\" to invalidate pact caches (setup.ini, etc.)"
  echo "Options:"
  echo "  --mirror, -m <url> : set mirror"
  echo "  --invalidate, -i       : invalidates pact caches (setup.ini, etc.)"
  echo "  --force, -f : force the execution"
  echo "  --help"
  echo "  --version"
}



function version()
{
  echo "pact version 1.1.0 (based on apt-cyg 0.57)"
  echo "Tweaked and maintained by Tom Bujok (@tombujok)"
  echo "Copyright (c) 2014-2016. Released under the MIT."
}


function findworkspace()
{
  # default working directory and mirror
  
  mirror=$PACT_REPO
  if [[ -z $mirror ]]; then
  echo "WARNINIG! ~/.pact/pact.repo does not contains \$PACT_REPO mirror address. Using the default one!"
  mirror=http://mirrors.kernel.org/sourceware/cygwin/
  fi
  cache=/setup
  
  # work wherever setup worked last, if possible
  mirrordir="`echo "$mirror" | sed -e "s/:/%3a/g" -e "s:/:%2f:g"`"
  
  echo Working directory is $cache
  echo Mirror is $mirror
  mkdir -p "$cache/$mirrordir"
  cd "$cache/$mirrordir"
}


function getsetup() 
{
#
  currdir="$(pwd)"
  if test "$noscripts" == "0"
  then
    if test "$invalidate" == "0" && [ -s "$currdir/setup.ini" ] && [ -s "$currdir/setup.ini-save" ] && [ $(find "$currdir" -mtime 0 -type f -regex '.*setup.ini-save' 2>/dev/null) ] && cmp -s "$currdir/setup.ini" "$currdir/setup.ini-save" ; then
    echo "setup.ini taken from the cache"
  else
    touch setup.ini
    mv setup.ini setup.ini-save
    wget --user-agent="$USER_AGENT" -N $mirror/$CYGWIN_VERSION/setup.bz2
    if test -e setup.bz2 && test $? -eq 0
    then
      bunzip2 setup.bz2
      mv setup setup.ini
      echo Updated setup.ini
    else
      wget --user-agent="$USER_AGENT" -N $mirror/$CYGWIN_VERSION/setup.ini
      if test -e setup.ini && test $? -eq 0
      then
      echo Updated setup.ini
      else
      mv setup.ini-save setup.ini
      echo Error updating setup.ini, reverting
      fi
    fi
  fi
  fi
}


function checkpackages()
{
  if test "-$packages-" = "--"
  then
    echo Nothing to do, exiting
    exit 0
  fi
}

# process options
noscripts=0
invalidate=0
file=""
command=""
filepackages=""
packages=""
force=0

function checkCygwin() {
  if test "$noscripts" == "0"
  then
    # Check if the underlying Cygwin distribution is up to date
    local newest_cygwin_version=$( get_newest_cygwin_version )
    if [[ -z "$newest_cygwin_version" ]]; then 
      echo -e "[WARNING] Could not check the Cygwin distribution version. Installed packages may not work!"
      return
    else
      local newest_cygwin_version_number=$( get_version_as_number $newest_cygwin_version )
      local current_cygwin_version=$( get_current_cygwin_version )
      local current_cygwin_version_number=$( get_version_as_number $current_cygwin_version )
      if [[ $newest_cygwin_version_number -gt $current_cygwin_version_number ]]; then
        if [[ force -eq 1 ]]; then
            echo -e "CRITICAL: The underlying Cygwin version is outdated! Force option enabled - continuing!"
        else 
            echo -e "-------------------------------------------------------------------------------"
            echo -e "CRITICAL: The underlying Cygwin version is outdated!"
            echo -e "It's forbidden to install new packages as they may fail to work." 
            echo -e "Execute 'babun update' and follow the instructions to update Cygwin."
            echo -e "If you know what you are doing add '--force' flag to proceed."
            echo -e "-------------------------------------------------------------------------------"
            exit 1
        fi
      fi    
    fi
  fi
}

function checkPackageCompatibility() {
  for pkg in $packages
    do      
      if [[ $pkg == "git" ]]; then
          if [[ force -eq 1 ]]; then
             echo -e "CRITICAL: Installing git in force mode..." 
           else
            echo -e "-------------------------------------------------------------------------------"
            echo -e "CRITICAL: Git cannot be updated in this version!"
            echo -e "Due to the bug https://github.com/babun/babun/issues/455 it's forbidden to install "
            echo -e "git as it may not work correctly behind ntlm proxy."             
            echo -e "If you know what you are doing add '--force' flag to proceed."
            echo -e "-------------------------------------------------------------------------------"
            exit 1
          fi
      fi
    done
}

function installPkg()
{

 for pkg in $packages
    do

    already=`grep -c "^$pkg " /etc/setup/installed.db`
    if test $already -ge 1
    then
      echo Package $pkg is already installed, skipping
      continue
    fi
    echo ""
    echo Installing $pkg

    # look for package and save desc file

    mkdir -p "release/$pkg"
    cat setup.ini | awk > "release/$pkg/desc" -v package="$pkg" \
      'BEGIN{RS="\n\n@ "; FS="\n"} {if ($1 == package) {desc = $0; px++}} \
       END {if (px == 1 && desc != "") print desc; else print "Package not found"}' 
    
    desc=`cat "release/$pkg/desc"`
    if test "-$desc-" = "-Package not found-"
    then
      echo Package $pkg not found or ambiguous name, exiting
      rm -r "release/$pkg"
      exit 1
    fi
    echo Found package $pkg

    # download and unpack the bz2 file

    # pick the latest version, which comes first
    install=`cat "release/$pkg/desc" | awk '/^install: / { print $2; exit }'` 

    if test "-$install-" = "--"
    then
      echo "Could not find \"install\" in package description: obsolete package?"
      exit 1
    fi

    file=`basename $install`
    cd "release/$pkg"
    wget --user-agent="$USER_AGENT" -nc $mirror/$install
    
    # check the verification hash (md5 or sha512)
    digest=`cat "desc" | awk '/^install: / { print $4; exit }'` 
    md5digactual=`md5sum $file | awk '{print $1}'`
    if ! [[ $digest == $md5digactual ]]
    then
      shadigactual=`sha512sum $file | awk '{print $1}'`
      if ! [[ $digest == $shadigactual ]]
      then
        echo Verification hash did not match, exiting
        rm $file # Remove the file with bad checksum
        exit 1
      fi
    fi
    
    echo "Unpacking..."
    #determine file type
    if [ "${file##*.}" == "xz" ]; then
      cat $file | tar > "/etc/setup/$pkg.lst" xvJf - -C /
    else
      cat $file | bunzip2 | tar > "/etc/setup/$pkg.lst" xvf - -C /
    fi
    gzip -f "/etc/setup/$pkg.lst"
    cd ../..
    
    
    # update the package database
    
    cat /etc/setup/installed.db | awk > /tmp/awk.$$ -v pkg="$pkg" -v bz=$file \
      '{if (ins != 1 && pkg < $1) {print pkg " " bz " 0"; ins=1}; print $0} \
       END{if (ins != 1) print pkg " " bz " 0"}'
    mv /etc/setup/installed.db /etc/setup/installed.db-save
    mv /tmp/awk.$$ /etc/setup/installed.db
    
    
    # recursively install required packages
    
    echo > /tmp/awk.$$ '/^requires: / {s=gensub("(requires: )?([^ ]+) ?", "\\2 ", "g", $0); print s}'
    requires=`cat "release/$pkg/desc" | awk -f /tmp/awk.$$`
    
    warn=0
    if ! test "-$requires-" = "--"
    then
      echo Package $pkg requires the following packages, installing:
      echo $requires
      for package in $requires
      do
        already=`grep -c "^$package " /etc/setup/installed.db`
        if test $already -ge 1
        then
          echo Package $package is already installed, skipping
          continue
        fi
        pact --noscripts install $package
        if ! test $? = 0 ; then warn=1; fi
      done
    fi
    if ! test $warn = 0
    then
      echo "Warning: some required packages did not install, continuing"
    fi
    
    # run all postinstall scripts

    if ! test $noscripts -eq 1
    then
      for script in `find /etc/postinstall/ -name *.sh`
      do
        echo Running postinstall script: $script
        $script
        mv $script $script.done
      done
    fi
    
    echo Package $pkg installed

    done
}

function removePkg()
{

 for pkg in $packages
    do

    already=`grep -c "^$pkg " /etc/setup/installed.db`
    if test $already = 0
    then
      echo Package $pkg is not installed, skipping
      continue
    fi

    dontremove="cygwin coreutils gawk bzip2 tar wget bash"
    for req in $dontremove
    do
      if test "-$pkg-" = "-$req-"
      then
        echo pact cannot remove package $pkg, exiting
        exit 1
      fi
    done

    if ! test -e "/etc/setup/$pkg.lst.gz"
    then
      echo Package manifest missing, cannot remove $pkg.  Exiting
      exit 1
    fi
    echo Removing $pkg

    # run preremove scripts

    if test -e "/etc/preremove/$pkg.sh"
    then
      "/etc/preremove/$pkg.sh"
      rm "/etc/preremove/$pkg.sh"
    fi

    cat "/etc/setup/$pkg.lst.gz" | gzip -d | awk '/[^\/]$/ {print "rm -f \"/" $0 "\""}' | sh
    rm "/etc/setup/$pkg.lst.gz"
    rm -f /etc/postinstall/$pkg.sh.done
    cat /etc/setup/installed.db | awk > /tmp/awk.$$ -v pkg="$pkg" '{if (pkg != $1) print $0}'
    mv /etc/setup/installed.db /etc/setup/installed.db-save
    mv /tmp/awk.$$ /etc/setup/installed.db
    echo Package $pkg removed

    done  
}

while test $# -gt 0
do
  case "$1" in

    --mirror|-m)
      echo "$2" > /etc/setup/last-mirror
      PACT_REPO="$2"
      echo "Mirror manually set to $PACT_REPO"
      shift ; shift
    ;;

    --noscripts)
      noscripts=1
      shift
    ;;

    --force|-f)
      force=1
      shift
    ;;

    --invalidate|-i)
      invalidate=1
      shift
    ;;

    --help)
      usage
      exit 0
    ;;

    --version)
      version
      exit 0
    ;;


    invalidate|update|show|find|describe|packageof|install|remove)
      if test "-$command-" = "--"
      then
        command=$1
      else
        packages="$packages $1"
      fi
      shift

    ;;

    *)
      packages="$packages $1"
      shift

    ;;

  esac
done

test $noscripts -eq 0 && check_only_one_running "pact"

case "$command" in

  invalidate)
  
  invalidate=1
    findworkspace
    getsetup

  ;;

  show)

    echo 1>&2 The following packages are installed:
    cat /etc/setup/installed.db | awk '/[^ ]+ [^ ]+ 0/ {print $1}'

  ;;


  find)

    checkpackages
    findworkspace
    getsetup

    for pkg in $packages
    do
      echo ""
      echo Searching for installed packages matching $pkg:
      awk '/[^ ]+ [^ ]+ 0/ {if ($1 ~ query) print $1}' query="$pkg" /etc/setup/installed.db
      echo ""
      echo Searching for installable packages matching $pkg:
      cat setup.ini | awk -v query="$pkg" \
        'BEGIN{RS="\n\n@ "; FS="\n"; ORS="\n"} {if ($1 ~ query) {print $1}}'
    done

  ;;


  describe)

    checkpackages
    findworkspace
    getsetup
    for pkg in $packages
    do
      echo ""
      cat setup.ini | awk -v query="$pkg" \
        'BEGIN{RS="\n\n@ "; FS="\n"; ORS="\n"} {if ($1 ~ query) {print $0 "\n"}}'
    done

  ;;


  packageof)

    checkpackages
    for pkg in $packages
    do
      key=`which "$pkg" 2>/dev/null | sed "s:^/::"`
      if test "-$key-" = "--"
      then
        key="$pkg"
      fi
      for manifest in /etc/setup/*.lst.gz
      do
        found=`cat $manifest | gzip -d | grep -c "$key"`
        if test $found -gt 0
        then
          package=`echo $manifest | sed -e "s:/etc/setup/::" -e "s/.lst.gz//"`
          echo Found $key in the package $package
        fi
      done
    done

  ;;


  install)
    
    checkCygwin
    checkPackageCompatibility
    checkpackages
    findworkspace
    getsetup
    installPkg

  ;;


  update)
    
    checkCygwin
    checkPackageCompatibility
    removePkg
    checkpackages
    findworkspace
    getsetup
    installPkg

  ;;

  remove)
    
    checkpackages
    removePkg

  ;;

  *)

    usage



  ;;

esac



